/*
* Copyright (c) 2011 - Georgios Gousios <gousiosg@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package gr.gousiosg.javacg.dyn;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.NotFoundException;
public class Instrumenter implements ClassFileTransformer {
static List<Pattern> pkgIncl = new ArrayList<>();
static List<Pattern> pkgExcl = new ArrayList<>();
public static void premain(String argument, Instrumentation instrumentation) {
// incl=com.foo.*,gr.bar.foo;excl=com.bar.foo.*
if (argument == null) {
err("Missing configuration argument");
return;
}
err("Argument is: " + argument);
String[] tokens = argument.split(";");
if (tokens.length < 1) {
err("Missing delimiter ;");
return;
}
for (String token : tokens) {
String[] args = token.split("=");
if (args.length < 2) {
err("Missing argument delimiter =:" + token);
return;
}
String argtype = args[0];
if (!argtype.equals("incl") && !argtype.equals("excl")) {
err("Wrong argument: " + argtype);
return;
}
String[] patterns = args[1].split(",");
for (String pattern : patterns) {
Pattern p = null;
err("Compiling " + argtype + " pattern:" + pattern + "$");
try {
p = Pattern.compile(pattern + "$");
} catch (PatternSyntaxException pse) {
err("pattern: " + pattern + " not valid, ignoring");
}
if (argtype.equals("incl"))
pkgIncl.add(p);
else
pkgExcl.add(p);
}
}
instrumentation.addTransformer(new Instrumenter());
}
public byte[] transform(ClassLoader loader, String className, Class clazz,
java.security.ProtectionDomain domain, byte[] bytes) {
boolean enhanceClass = false;
String name = className.replace("/", ".");
for (Pattern p : pkgIncl) {
Matcher m = p.matcher(name);
if (m.matches()) {
enhanceClass = true;
break;
}
}
for (Pattern p : pkgExcl) {
Matcher m = p.matcher(name);
if (m.matches()) {
err("Skipping class: " + name);
enhanceClass = false;
break;
}
}
if (enhanceClass) {
return enhanceClass(className, bytes);
} else {
return bytes;
}
}
private byte[] enhanceClass(String name, byte[] b) {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = null;
try {
clazz = pool.makeClass(new ByteArrayInputStream(b));
if (!clazz.isInterface()) {
err("Enhancing class: " + name);
CtBehavior[] methods = clazz.getDeclaredBehaviors();
for (int i = 0; i < methods.length; i++) {
if (!methods[i].isEmpty()) {
enhanceMethod(methods[i], clazz.getName());
}
}
b = clazz.toBytecode();
}
} catch (CannotCompileException e) {
e.printStackTrace();
err("Cannot compile: " + e.getMessage());
} catch (NotFoundException e) {
e.printStackTrace();
err("Cannot find: " + e.getMessage());
} catch (IOException e) {
err("Error writing: " + e.getMessage());
} finally {
if (clazz != null) {
clazz.detach();
}
}
return b;
}
private void enhanceMethod(CtBehavior method, String className)
throws NotFoundException, CannotCompileException {
String name = className.substring(className.lastIndexOf('.') + 1, className.length());
String methodName = method.getName();
if (method.getName().equals(name))
methodName = "<init>";
method.insertBefore("gr.gousiosg.javacg.dyn.MethodStack.push(\"" + className
+ ":" + methodName + "\");");
method.insertAfter("gr.gousiosg.javacg.dyn.MethodStack.pop();");
}
private static void err(String msg) {
//System.err.println("[JAVACG-DYN] " + msg);
}
}